using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace AVR109Boot
{
    /// <summary>
    /// This class is used to translate the output files from AVR-GCC to an array of bytes for use by the bootloader
    /// </summary>
    public class IntelHexDecoder
    {
        private int FlashLength;
        private string errors;
        /// <summary>
        /// Errors that occured during this instance
        /// </summary>
        public string Errors { get { return errors; } }

        private byte[] output;
        /// <summary>
        /// An array of bytes representing the data encoded in the input hex file padded out with 0xFF to the length of the flash.
        /// </summary>
        public byte[] Output
        {
            get { return output; }
        }
        /// <summary>
        /// Create a new IntelHexdecoder.
        /// </summary>
        /// <param name="FlashLength">Number of (8 bit) bytes of flash memory in your microcontroller</param>
        public IntelHexDecoder(int FlashLength)
        {
            this.FlashLength = FlashLength;
            output = new byte[FlashLength];
            for (int i = 0; i < FlashLength; i++)
            {
                output[i] = 0xff;//init all bytes
            }
        }
        /// <summary>
        /// Get the decoded version of the hex file as an array of bytes
        /// </summary>
        /// <param name="Input">The contents of the hex file</param>
        /// <returns>The decoded hex program</returns>
        public byte[] Decode(string Input)
        {
            int Length = 0;
            byte[] program = null;
            UInt16 baseAddress = 0;
            bool firstData = true;
            string[] lines = Input.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string line in lines)
            {

                byte ByteCount = Convert.ToByte(line.Substring(0, 2), 16);
                byte[] Data = new byte[ByteCount];
                UInt16 Address = Convert.ToUInt16(line.Substring(2, 4), 16);
                byte RecordType = Convert.ToByte(line.Substring(6, 2), 16);
                UInt16 DataSum = 0;
                int j = 8;

                for (int i = 0; i < ByteCount; i++)
                {
                    Data[i] = Convert.ToByte(line.Substring(j, 2), 16);
                    if (Address + i+1 > FlashLength)
                    {
                        errors += "Flash length (" + FlashLength + ") is smaller than memory allocated in the hex file\r\n";
                        return null;
                    }
                    DataSum += Data[i];
                    j += 2;
                    if (RecordType == 0x00)
                    {
                        if (firstData)
                        {
                            baseAddress = Address;
                            firstData = false;
                        }
                        if (i % 2 == 0)
                        {
                            output[Address + i+1] = Data[i];
                        }
                        else
                        {
                            output[Address + i-1] = Data[i];
                        }
                    }
                    Length++;
                }

                byte CheckSum = Convert.ToByte(line.Substring(ByteCount * 2 + 8, 2), 16);//2 chars/byte
                
                byte Total = (byte)(((((ByteCount + (byte)((Address >> 8) & 0xFF) + (byte)(Address & 0xFF) + RecordType + DataSum) & 0xFF) ^ 0xFF) + 1) & 0xFF);
                if (Total != CheckSum)
                {
                    errors += "invalid checksum\r\n";
                    return null;
                }

            }
            program = new byte[Length + baseAddress];
            for (int i = 0; i < program.Length; i++)
            {
                program[i] = output[i];
            }
            return program;
        }
    }
}
